;; 启用最近打开的文件历史
(leaf recentf
  :commands recentf-save-list
  :setq-default ((recentf-max-menu-items . 50)
                 (recentf-max-saved-items . 50))
  :setq `((recentf-save-file . ,(cache-path "recentf")))
  :config
  (recentf-mode t))

;; TODO 替换成更高级的
(leaf my-surround :require t)

;; insert-date
(defun insert-date ()
  "Insert date at point."
  (interactive)
  (insert (format-time-string "%Y年%m月%e日 %A %p %I:%M")))

;; ivy
(leaf ivy
  :ensure t
  :bind (("<backtab>" . ivy-switch-buffer)
         ("<C-tab>" . ivy-switch-buffer)
         ("<f6>" . ivy-resume))
  :setq ((ivy-use-virtual-buffers . t)
         (ivy-virtual-abbreviate . 'full)
         (enable-recursive-minibuffers . t))
  :config
  (ivy-mode 1))

(leaf ivy-hydra :ensure t)

(leaf counsel
  :ensure t
  :preface
  (defun counsel-fzf-dir-function-projectile-git nil
    (if (and
         (fboundp 'projectile-project-p)
         (fboundp 'projectile-project-root)
         (projectile-project-p))
        (projectile-project-root)
      (or
       (counsel--git-root)
       default-directory)))
  :setq ((counsel-grep-base-command . counsel-rg-base-command)
         (counsel-fzf-dir-function . 'counsel-fzf-dir-function-projectile-git)
         (counsel-fzf-cmd . "fd --type f | sk -f \"%s\"")
         (counsel-yank-pop-separator . "\n-----\n")
         (enable-recursive-minibuffers . t))
  :bind (("C-s-o" . counsel-rhythmbox)
         ("C-x l" . counsel-locate)
         ("C-c s" . counsel-rg)
         ("C-c C-s" . counsel-rg)
         ("C-c f" . counsel-fzf)
         ("C-c C-f" . counsel-fzf)
         ("<f2> u" . counsel-unicode-char)
         ("<f2> i" . counsel-info-lookup-symbol)
         ("<f1> l" . counsel-find-library)
         ("<f1> v" . counsel-describe-variable)
         ("<f1> f" . counsel-describe-function)
         ("C-x C-f" . counsel-find-file)
         ("M-x" . counsel-M-x)
         ("M-y" . counsel-yank-pop)
         (minibuffer-local-map
          ("C-r" . counsel-minibuffer-history))))

(leaf swiper
  :ensure t
  :bind
  ("C-s" . swiper-isearch)
  :custom-face
  ((swiper-background-match-face-1 . '((t nil)))))

(leaf nerd-icons-ivy-rich
  :ensure t
  :after (nerd-icons)
  :init (nerd-icons-ivy-rich-mode 1))

(leaf ivy-rich
  :ensure t
  :after (nerd-icons nerd-icons-ivy-rich)
  :init
  (ivy-rich-mode 1)
  (ivy-rich-project-root-cache-mode))

;; Better sorting and filtering
(leaf prescient
  :ensure t
  :commands prescient-persist-mode
  :setq (prescient-filter-method . '(literal regexp initialism))
  :config
  (setq prescient-save-file (cache-path "prescient-save.el"))
  (prescient-persist-mode 1))

(leaf ivy-prescient
  :ensure t
  :after (ivy counsel swiper)
  :pre-setq
  ((ivy-prescient-retain-classic-highlighting . t)
   (ivy-prescient-sort-commands
    .
    '(:not
      swiper
      swiper-isearch
      ivy-switch-buffer
      counsel-grep
      counsel-ag
      counsel-yank-pop)))
  :custom-face
  ((ivy-minibuffer-match-face-1
    .
    '((t (:inherit font-lock-doc-face :foreground unspecified)))))
  :config
  (ivy-prescient-mode 1)
  (setf (alist-get 'counsel-rg ivy-re-builders-alist) #'ivy--regex-plus))

;; 光标跳转
(leaf avy
  :ensure t
  :bind (("C-c j" . avy-goto-char-timer)
         ("C-c C-j" . avy-goto-char-timer)
         ("M-g M-g" . avy-goto-line))
  :setq ((avy-style . 'words)
         (avy-timeout-seconds . 3)))

;; 批量编辑, ivy-occur 需要
(leaf wgrep
  :ensure t
  :setq-default ((wgrep-auto-save-buffer . t)))

;; 文件目录结构显示
(leaf treemacs
  :ensure t
  :preface
  (defun treemacs-show? ()
    (and
     (fboundp 'treemacs-is-treemacs-window-selected?)
     (treemacs-is-treemacs-window-selected?)))
  :bind (("C-x C-d" . treemacs-select-window)
         (treemacs-mode-map
          ("s" . nil)
          ("S" . treemacs-resort)
          ("w" . nil)))
  :setq (treemacs-read-string-input . 'from-minibuffer)
  :init
  (treemacs-define-custom-image-icon (static-path "treemacs-icons/gpg.png") "gpg")
  :config
  (define-key treemacs-mode-map (kbd "w") treemacs-workspace-map)
  (treemacs-follow-mode t)
  (pcase (cons (not (null (executable-find "git")))
               (not (null (executable-find "python3"))))
    (`(t . t)
     (treemacs-git-mode 'deferred))
    (`(t . _)
     (treemacs-git-mode 'simple))))

(leaf treemacs-magit
  :after (treemacs magit)
  :require t
  :ensure t)

;; 智能删除空白字符
(leaf ws-butler
  :ensure t
  :config
  (ws-butler-global-mode))

;; 自动保存
(leaf super-save
  :ensure t
  :bind (("C-x C-s" . (lambda (&rest _) (interactive) (super-save-command))))
  :setq ((super-save-auto-save-when-idle . nil)
         (super-save-all-buffers . t))
  :preface
  (defvar super-save--buffer-list nil)

  ;; we don't use this function for delete whitespace, here we just use it as a hook
  (advice-add
   'super-save-delete-trailing-whitespace-maybe :override
   (lambda (&rest _)
     "Push current buffer name to `super-save--buffer-list'."
     (push (buffer-name) super-save--buffer-list)))

  (defun super-save--print-saved-files (&rest _)
    "Save the world, with decent info."
    (cond
     ;; It's stupid tell user if nothing to save.
     ((= (length super-save--buffer-list) 1)
      (message "# Saved %s" (car super-save--buffer-list)))
     ((> (length super-save--buffer-list) 1)
      (message "# Saved %d files: %s"
               (length super-save--buffer-list)
               (mapconcat 'identity super-save--buffer-list ", ")))))

  (advice-add 'super-save-command :before (lambda (&rest _) (setq super-save--buffer-list nil)))
  (advice-add 'super-save-command :after #'super-save--print-saved-files)
  :init
  (super-save-mode 1))

;; multiple cursors
(leaf multiple-cursors
  :ensure t
  :setq `((mc/list-file . ,(cache-path ".mc-lists.el")))
  :bind (("C-c C-SPC" . mc/mark-more-like-this-extended)
         ("C-c SPC" . mc/mark-more-like-this-extended)
         ("C-c C-/" . mc/edit-lines)))

;; 标记 高亮 highlight
(leaf symbol-overlay
  :ensure t
  :bind (("C-." . symbol-overlay-put)
         ("M-n" . symbol-overlay-switch-forward)
         ("M-p" . symbol-overlay-switch-backward)
         (symbol-overlay-map
          ("." . symbol-overlay-put))))

;; 窗口切换
(leaf ace-window
  :ensure t
  :preface
  (defun ace-window-treemacs-check (arg)
    (interactive "p")
    (if (treemacs-show?)
        (progn
          (other-window arg)
          (ace-window arg))
      (ace-window arg)))
  :setq ((aw-dispatch-always . t)
         (aw-dispatch-alist
          .
          '((?0 aw-delete-window "Delete Window")
	        (?s aw-swap-window "Swap Windows")
	        (?m aw-move-window "Move Window")
	        (?c aw-copy-window "Copy Window")
	        (?j aw-switch-buffer-in-window "Select Buffer")
	        (?n aw-flip-window "Select the previous window")
	        (?u aw-switch-buffer-other-window "Switch Buffer Other Window")
	        (?- aw-split-window-fair "Split Fair Window")
	        (?v aw-split-window-vert "Split Vert Window")
	        (?h aw-split-window-horz "Split Horz Window")
	        (?o delete-other-windows "Delete Other Windows")
	        (?? aw-show-dispatch-help)))
         (aw-posframe-position-handler . #'posframe-poshandler-window-top-left-corner))
  :bind (("M-o" . ace-window-treemacs-check)
         ("C-x o" . nil)
         ("C-x C-o" . nil)
         ("C-x 0" . nil)
         ("C-x 1" . nil)
         ("C-x 2" . nil)
         (ivy-minibuffer-map
          ("M-o" . nil)
          ("C-x o" . 'ivy-dispatching-done)))
  :config
  (set-face-attribute 'aw-leading-char-face nil
                      :height 5.0
                      :foreground "#51afef")
  `(if window-system (ace-window-posframe-mode)))

;; 窗口撤销
(leaf winner :config (winner-mode 1))

;; org 表格对齐
(leaf valign
  :vc (valign :url "https://github.com/casouri/valign.git")
  :hook (org-mode-hook markdown-mode-hook)
  :setq ((valign-fancy-bar . t)))

(leaf org-modern
  :ensure t
  :after org
  :setq ((org-modern-table . nil)
         (org-modern-keyword . "")
         (org-modern-block-fringe . 2)
         (org-modern-block-name . '("" . "")))
  :hook (org-mode-hook))

(leaf ox-confluence :after org :require t)

;; 中英文之间加空格
(leaf pangu-spacing
  :ensure t
  :commands pangu-spacing-search-buffer
  :hook (org-mode-hook)
  :setq `((pangu-spacing-real-insert-separtor . t)
          (pangu-spacing-include-regexp
           .
           ,(rx (or (and (or (group-n 3 (any "。，！？；：「」（）、"))
                             (group-n 1 (or (category chinese-two-byte)
                                            (category japanese-hiragana-two-byte)
                                            (category japanese-katakana-two-byte))))
                         (group-n 2 (in "a-zA-Z0-9")))
                    (and (group-n 1 (in "a-zA-Z0-9"))
                         (or (group-n 3 (any "。，！？；：「」（）、"))
                             (group-n 2 (or (category chinese-two-byte)
                                            (category japanese-hiragana-two-byte)
                                            (category japanese-katakana-two-byte))))))))))

(leaf rg
  :ensure t
  ;; :setq ((rg-group-result . nil))
  :commands rg)

;; Git 状态显示
(leaf diff-hl
  :ensure t
  :hook ((magit-post-refresh-hook . diff-hl-magit-post-refresh)
         ;; fix commit msg buffer not close after commit when
         ;; diff-hl-update-async is on
         (git-commit-mode-hook . (lambda () (diff-hl-mode -1))))
  :init
  (global-diff-hl-mode)
  (unless (window-system)
    (diff-hl-margin-mode)))

(leaf magit
  :ensure t
  :bind (("C-c v" . magit-status)
         ("C-c C-v" . magit-status)
         (magit-mode-map ("<C-tab>")))
  :setq `((magit-diff-refine-hunk . 'all)
          (transient-levels-file . ,(cache-path "transient/levels.el"))
          (transient-values-file . ,(cache-path "transient/values.el"))
          (transient-history-file . ,(cache-path "transient/history.el")))
  :config
  (magit-todos-mode))

(leaf git-commit-mode
  :after (magit)
  :config
  (define-key git-commit-mode-map (kbd "<escape>") #'god-local-mode))

(leaf forge
  :ensure t
  :require t
  :after (magit)
  :setq `((forge-database-file . ,(cache-path "forge-database.sqlite")))
  :config
  (set-face-attribute 'forge-topic-label nil
                      :box '(:line-width -3 :style flat-button)))

(leaf code-review
  :vc (code-review
       :url "https://github.com/phelrine/code-review.git"
       :branch "fix/closql-update")
  :setq (code-review-auth-login-marker . 'forge)
  :bind
  ((code-review-mode-map
    ("?" . code-review-transient-api)
    ("<C-tab>" . nil))
   (forge-pullreq-section-map
    :package forge
    ("<remap> <magit-visit-thing>" . code-review-forge-pr-at-point))
   (forge-topic-mode-map
    :package forge
    ("C-c r" . code-review-forge-pr-at-point))))

(leaf git-timemachine :ensure t)

(leaf hl-todo
  :ensure t
  :config
  (global-hl-todo-mode))

(leaf magit-todos
  :ensure t
  :after (magit)
  :setq
  ((magit-todos-rg-extra-args . '("-M 240")))
  :init
  (add-to-list 'magit-todos-exclude-globs "/snippets/**/todo"))

(leaf magit-delta
  :ensure t
  :after (magit)
  :setq (magit-delta-default-dark-theme . "DarkNeon")
  :hook (magit-mode-hook . magit-delta-mode))

(leaf git-link :ensure t)

;; auto switch ime
(leaf sis
  :ensure t
  :pre-setq ((sis-external-ism . "fcitx5-remote")
             (sis-english-source . "2")
             (sis-other-source . "1")
             (sis-other-cursor-color . "#da8548")
             (sis-inline-tighten-head-rule . 0)
             (sis-inline-tighten-tail-rule . 'one))
  :preface
  (defvar sis--god-mode-status 'on)
  (defvar-local sis--god-mode-current 'english)
  (defun describe-key-sis ()
    (interactive)
    (sis-set-english)
    (sis-global-respect-mode 0)
    (describe-key (help--read-key-sequence))
    (sis-global-respect-mode t))
  (defun sis--god-mode-enable-hook ()
    (unless (equal sis--god-mode-status 'on)
      (sis-prefix-override-buffer-disable)
      (setq-local sis--god-mode-current sis--for-buffer)
      (sis-set-english)
      (setq sis--god-mode-status 'on)))
  (defun sis--god-mode-disable-hook ()
    (unless (equal sis--god-mode-status 'off)
      (sis-prefix-override-buffer-enable)
      (setq sis--for-buffer-locked nil)
      (sis--set sis--god-mode-current)
      (setq-local sis--god-mode-current nil)
      (setq sis--god-mode-status 'off)))
  (defun sis--respect-post-cmd-timer-fn-before! ()
    ;; last time this buffer is not in god-mode
    ;; but this time it is in god-mode, so we need to
    ;; set current buffer to english mode, and save
    ;; last time's ime status
    (when (and (equal sis--god-mode-status 'on)
               (null sis--god-mode-current))
      (setq-local sis--god-mode-current sis--for-buffer)
      (setq-local sis--for-buffer 'english))
    ;; last time this buffer is in god-mode
    ;; but this time it is not in god-mode, so we need to
    ;; set current sis status to `sis--god-mode-current'
    ;; and set it to nil
    (when (and (equal sis--god-mode-status 'off)
               (not (null sis--god-mode-current)))
      (setq-local sis--for-buffer sis--god-mode-current)
      (setq-local sis--god-mode-current nil)))
  (defun sis--inline-ret-check-to-deactivate! (fun &rest args)
    "Do not delete space when press RET in inline mode."
    (let ((sis-inline-tighten-tail-rule 0))
      (apply fun args)))
  (advice-add 'sis--inline-ret-check-to-deactivate :around
              #'sis--inline-ret-check-to-deactivate!)
  (advice-add 'sis--respect-post-cmd-timer-fn :before
              #'sis--respect-post-cmd-timer-fn-before!)
  :init
  (setq sis-do-get (lambda nil
                     (string-trim
                      (shell-command-to-string sis-external-ism))))
  (setq sis-do-set (lambda (source)
                     (pcase source
                       ("1"
                        (string-trim
                         (shell-command-to-string
                          (concat sis-external-ism " -c"))))
                       ("2"
                        (string-trim
                         (shell-command-to-string
                          (concat sis-external-ism " -o")))))))
  :require t
  :bind (("C-h k" . describe-key-sis))
  :config
  (push "<C-tab>" sis-prefix-override-keys)
  (push "M-g" sis-prefix-override-keys)
  (push "M-o" sis-prefix-override-keys)
  ;; turn off prefix override when first enter a buffer with god-mode
  (push (lambda () (bound-and-true-p god-local-mode))
        sis-prefix-override-buffer-disable-predicates)
  (sis-global-respect-mode t)
  (sis-global-inline-mode t)
  (sis-global-cursor-color-mode t)
  (add-hook 'sis-inline-english-deactivated-hook
            (lambda nil
              (if sis-inline-with-english
                  (sis-set-other))))
  (add-hook 'god-mode-enabled-hook #'sis--god-mode-enable-hook)
  (add-hook 'god-mode-disabled-hook #'sis--god-mode-disable-hook)
  (sis-auto-refresh-mode 0)
  (add-to-list 'http-server-callbacks
               (lambda (&rest _) (sis--save-to-buffer))))

(leaf god-mode
  :ensure t
  :pre-setq ((god-mode-enable-function-key-translation . nil))
  :bind (("<escape>" . god-mode-all)
         ("M-j" . nil)
         (god-local-mode-map
          ("j" . repeat)))
  :preface
  (defun my-god-mode-update-cursor-type ()
    (unless (treemacs-show?)
      (setq cursor-type (if god-local-mode '(bar . 3) 'box))))
  :init
  (god-mode)
  (add-hook 'god-mode-enabled-hook #'my-god-mode-update-cursor-type)
  (add-hook 'god-mode-disabled-hook #'my-god-mode-update-cursor-type)
  (unless (window-system) (god-mode)))

(leaf lsp-grammarly
  :ensure t
  :after (lsp-mode))

(leaf atomic-chrome
  :ensure t
  :setq
  ((atomic-chrome-url-major-mode-alist
    .
    '(("github\\.com" . gfm-mode)
      ("emacs-china\\.org" . gfm-mode)
      ("redmine" . textile-mode)))
   (atomic-chrome-buffer-open-style . 'frame)
   (atomic-chrome-new-frame-alist
    .
    '((name . "")
      (fullscreen . nil)
      (ns-transparent-titlebar . t)
      (top . 0.4)
      (height . 0.3)
      (left . 0.35)
      (width . 0.35))))
  :preface
  (defun atomic-chrome-show-edit-buffer! (fun &rest args)
    (cl-letf ((orginal-make-frame-fun
               (symbol-function 'make-frame))
              (orginal-make-frame-on-display
               (symbol-function 'make-frame-on-display))
              ((symbol-function 'make-frame)
               (lambda (&rest _)
                 (funcall orginal-make-frame-fun atomic-chrome-new-frame-alist)))
              ((symbol-function 'make-frame-on-display)
               (lambda (display _ &rest _)
                 (funcall orginal-make-frame-on-display display atomic-chrome-new-frame-alist))))
      (apply fun args)))
  (advice-add 'atomic-chrome-show-edit-buffer :around #'atomic-chrome-show-edit-buffer!)
  :init
  (if after-init-time
      (atomic-chrome-start-server)
    (add-hook
     'after-init-hook
     #'(lambda () (atomic-chrome-start-server)))))

(provide 'edit/advanced)
